#include "camera_api.h"

#if USE_CRANE_CAMERA

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>

#include "camera_hal_core.h"
#include "camera_api.h"
#include "camera_common_log.h"
#include "camera_config.h"
#include "lv_drivers/display/scaler.h"
#include "lvgl/src/lv_misc/lv_color.h"

static int camera_sensor_id = CAMERA_BACK;

#if USE_CRANE_CAMERA_MULTI
void mci_camera_set_sensor(int sensor)
{
    if(sensor == CAMERA_SENSOR_BACK) {
        camera_sensor_id = CAMERA_BACK;
    } else if(sensor == CAMERA_SENSOR_FRONT) {
        camera_sensor_id = CAMERA_FRONT;
    }
}

int mci_camera_get_sensor(void)
{
  if(camera_sensor_id == CAMERA_BACK) {
      return CAMERA_SENSOR_BACK;
  } else if(camera_sensor_id == CAMERA_FRONT) {
      return CAMERA_SENSOR_FRONT;
  }
  return -1;
}
#endif

static CCaptureParamStruct capture_info = {0};
static void mci_camera_capture_exif(uint8_t *exif_buf, uint32_t exif_size)
{
    capture_info.jpeg_size = exif_size;
    CAMLOGD("mci_camera_capture_exif,buf=%p,size=%d!", exif_buf, capture_info.jpeg_size);
}

static void mci_camera_capture_update(uint16_t width, uint16_t height, uint8_t *jpeg_buf, uint32_t jpeg_size)
{
    capture_info.width = width;
    capture_info.height = height;
    capture_info.jpeg_size += jpeg_size;
    CAMLOGD("mci_camera_capture_update, wxh:%dx%d,buf=%p,size=%d!", capture_info.width, capture_info.height, jpeg_buf, capture_info.jpeg_size);
}

static void mci_camera_get_info(uint32_t *lcd_w, uint32_t *lcd_h, uint16_t *rot_degree)
{
    *lcd_w = PREVIEW_SNAPSHOT_W;
    *lcd_h = PREVIEW_SNAPSHOT_H;
    *rot_degree = ROT_0_DEG;

#if CAMERA_ROTATION_ENABLE
#if CAMERA_ROT_DEG90_FLAG
    *rot_degree = ROT_90_DEG;
#else
    *rot_degree = ROT_270_DEG;
#endif
#endif
    CAMLOGD("mci_camera_get_info, lcd wxh:%dx%d, rot=%d!", *lcd_w, *lcd_h, *rot_degree);
}

extern UINT32 MCI_CamPowerUp (INT32  camera_id, void (*cb)(INT32));
extern UINT32  MCI_CamPowerDown (void) ; //MCI_CAM_POWER_DOWN_REQ;
extern UINT32  MCI_CamPreviewOpen(CAM_PREVIEW_STRUCT* data);//MCI_CAM_PREVIEW_REQ
extern UINT32  MCI_CamPreviewClose (void) ; //MCI_CAM_STOP_REQ
extern UINT32  MCI_CamCapture (CAM_CAPTURE_STRUCT *data);    //MCI_CAM_CAPTURE_REQ
extern INT32  MCI_CamSavePhoto (INT32 file_handle); //MCI_CAM_CAPTURE_REQ
extern UINT32  MCI_CamSetPara(INT32 effectCode,INT32 value) ; // MCI_CAM_SET_PARAM_REQ
extern UINT32  MCI_CamStartFullSizeStream (CAM_RECORD_PARMETERS_STRUCT *pRecordParam, uint32_t format);
extern UINT32  MCI_CamStopFullSizeStream (void);
extern UINT32 MCI_CamFullSizeStreamSaveBuffer(char *data, uint32_t data_len);
extern UINT32 MCI_CamFullSizeStreamCapture(INT32 file_handle, void *capture_data);
extern UINT32  MCI_CamOpenATA (INT32 camera_id);
extern UINT32  MCI_CamCloseATA (INT32 camera_id);
extern BOOL CameraCallbackRegister(void* get_info, void* pre_cb, void* exif_cb, void* cap_cb);
extern int cam_offline_init(void);
extern int cam_offline_deinit(void);
extern int cam_offline_rotation(struct cam_offline_buf* buf_src, struct cam_offline_buf* buf_dst, enum offline_rot rot, int module_id);
extern int cam_offline_scaler_zoom(struct cam_offline_buf* buf_src, struct cam_offline_buf* buf_dst, int zoom_level, int module_id);
extern int cam_offline_homo_zoom(struct cam_offline_buf* buf_src, struct cam_offline_buf* buf_dst,int zoom_level, int module_id);
extern int cam_hw_version_init(void);
extern int MCI_CamSWRgb565Rotate(char *des, char *src, uint32_t srcWidth, uint32_t srcHeight, uint8_t rotate);
extern int MCI_CamSWYuv420ToRgb565(uint16_t *pRgbData, uint32_t rgbWidth, uint32_t rgbHeight, char *pYuvData, uint32_t yuvWidth, uint32_t yuvHeight, uint8_t yuvFormat, uint8_t is_swap);
extern int MCI_CamSWYuv420nv12Rotate(char *pDst, char *pSrc, uint32_t srcWidth, uint32_t srcHeight, uint8_t rotate);
extern int MCI_CamSWYuv420nv12ScalerDown(char *pDstData, uint32_t dstWidth, uint32_t dstHeight, char *pSrcData, uint32_t srcWidth, uint32_t srcHeight);
extern BOOL IsChipCraneM(void);
extern BOOL IsChipCraneL(void);
extern int MCI_CamGetIspSize (CAM_ISP_SIZE_STRUCT *ispSize);

static void setReleaseBufferCb(ReleaseBufferCallback cb, void *userData);
static void EnqueueCamBuffer(void *pBuf, int bufIndex);
static void FlushCamBuffers(void);

static char *g_mci_cam_yuv420_scaler_ptr = NULL;
static CAM_ISP_SIZE_STRUCT g_mci_cam_isp_size = {0x0};

#if CAMERA_ROTATION_ENABLE
static char *g_mci_cam_yuv420_rotate_ptr = NULL;
//#define MCI_CAM_ROTATE_90   (1)
//#define MCI_CAM_ROTATE_180  (2)
//#define MCI_CAM_ROTATE_270  (3)
#endif

int common_offline_scaler(struct cam_offline_buf* buf_src, struct cam_offline_buf* buf_dst, unsigned int module_id)
{
    return cam_offline_scaler_zoom(buf_src, buf_dst, 0x100, module_id);
}

void mci_camera_register_init(void)
{
    CameraCallbackRegister(mci_camera_get_info, mci_camera_update, mci_camera_capture_exif, mci_camera_capture_update);
}

int mci_camera_openpreview(void)
{
    CCError error = CRANE_CAMERA_ERROR_NONE;
    CAM_PREVIEW_STRUCT data;
    int sensor_id = camera_sensor_id;
#if USE_CRANE_CAMERA_FULL_SIZE
    BOOL is_full_size_stream = true;
#else
    BOOL is_full_size_stream = false;
#endif

    if (IsChipCraneM() || IsChipCraneL())
    {
        is_full_size_stream = true;
    }
    if (is_full_size_stream)
    {
        CAM_RECORD_PARMETERS_STRUCT record_parms = {0};
        record_parms.enqueueCamRecordBuffer = EnqueueCamBuffer;
        record_parms.setReleaseBufferCallBack = setReleaseBufferCb;
        record_parms.flushCamRecordBuffers = FlushCamBuffers;
        error = mci_camera_start_full_size_stream(&record_parms, PIPE_OUT_FMT_YUV420_NV12);
        if (error != CRANE_CAMERA_ERROR_NONE) {
            CAMLOGE("mci_camera_start_full_size_stream the %d sensor failed for %d!", sensor_id, error);
            mci_camera_stop_full_size_stream();
            return -1;
        }
        return 0;
    }

    CAMLOGV("ready to open camera!");
    error = (CCError)MCI_CamPowerUp(sensor_id, 0);

    if ( (error != CRANE_CAMERA_ERROR_NONE) && (error != CRANE_CAMERA_ERROR_DEVICEWASOPENED) ) {
        CAMLOGE("MCI_CamPowerUp the %d sensor failed for %d!", sensor_id, error);
        return -1;
    }
    memset(&data, 0, sizeof(CAM_PREVIEW_STRUCT));
    data.start_x = 0;
    data.start_y = 0;
    data.end_x         = PREVIEW_SNAPSHOT_W;
    data.end_y         = PREVIEW_SNAPSHOT_H;
    data.image_width   = data.end_x - data.start_x;
    data.image_height  = data.end_y - data.start_y;
    data.factor        = 0;

    CAMLOGV("ready to start preview!");
    error = (CCError)MCI_CamPreviewOpen((CAM_PREVIEW_STRUCT*)&data);
    if (error != CRANE_CAMERA_ERROR_NONE) {
        CAMLOGE("CCStartPreview the %d sensor failed for %d!", sensor_id, error);
        goto Error_exit;
    }

    return 0;

Error_exit:
    MCI_CamPowerDown();
//    lcd_poweroff();
    CAMLOGE("Open the %d camera Error", sensor_id);
    return -1;
}

void mci_camera_stoppreview(void)
{
#if USE_CRANE_CAMERA_FULL_SIZE
    BOOL is_full_size_stream = true;
#else
    BOOL is_full_size_stream = false;
#endif

    CAMLOGD("%s",__FUNCTION__);
    if (IsChipCraneM() || IsChipCraneL())
    {
        is_full_size_stream = true;
    }
    if (is_full_size_stream)
    {
        mci_camera_stop_full_size_stream();
        return;
    }

    MCI_CamPreviewClose();
    MCI_CamPowerDown();
}

int mci_camera_capture(void *param, int file_handle)
{
    CCCaputreStruct capture_data;
    CCError error = CRANE_CAMERA_ERROR_NONE;
    CCaptureParamStruct *cap_param = (CCaptureParamStruct *)param;

    CAMLOGE("%s",__FUNCTION__);
    capture_data.image_width = ((cap_param->width > 0) ? cap_param->width : PREVIEW_SNAPSHOT_W); ///240 //320
    capture_data.image_height = ((cap_param->height > 0) ? cap_param->height : PREVIEW_SNAPSHOT_H); ///240

    if (IsChipCraneM() || IsChipCraneL())
    {
        error = (CCError)MCI_CamFullSizeStreamCapture(file_handle, (CAM_CAPTURE_STRUCT *)&capture_data);
    }
    else
    {
        error = (CCError)MCI_CamCapture((CAM_CAPTURE_STRUCT *)&capture_data);
        if (error == CRANE_CAMERA_ERROR_NONE)
        {
            error = MCI_CamSavePhoto(file_handle);
        }
    }

    if (error == CRANE_CAMERA_ERROR_NONE)
    {
        cap_param->width = capture_info.width;
        cap_param->height = capture_info.height;
        cap_param->jpeg_size = capture_info.jpeg_size;
    }
    mci_camera_preview_reset();

    return error;
}

typedef struct camera_info_s {
    uint8_t dirty;
    uint8_t is_semi;
    uint16_t width;
    uint16_t height;
    uint8_t *y_buf;
    uint8_t *uv_buf;
}camera_info_t;

static camera_info_t camera_info;

static void mci_camera_update(uint16_t width,
                   uint16_t height,
                   uint8_t *buf1,
                   uint8_t *buf2,
                   uint8_t semi)
{
    camera_info.dirty = 1;
    camera_info.width = width;
    camera_info.height = height;
    camera_info.y_buf = buf1;
    camera_info.uv_buf = buf2;
    camera_info.is_semi = semi;
    CAMLOGD("mci_camera_update, wxh:%dx%d, y_buf=0x%x!", camera_info.width, camera_info.height, camera_info.y_buf);
}

static ReleaseBufferCallback g_cam_callbk = NULL;
static void  *g_cam_bufq = NULL;

static void setReleaseBufferCb(ReleaseBufferCallback cb, void *userData)
{
    CAMLOGD("setSourceCallBack \n");
    g_cam_callbk = cb;
    g_cam_bufq = userData;
}

static void EnqueueCamBuffer(void *pBuf, int bufIndex)
{
    uint32_t img_w = g_mci_cam_isp_size.width;
    uint32_t img_h = g_mci_cam_isp_size.height;
    char * yBuf = pBuf;
    char * uvBuf = NULL;

    if (yBuf)
    {
        uvBuf = yBuf + img_w * img_h;
        //CAMLOGD("EnqueueCamBuffer, sensor-wxh:%dx%d, error=0x%x!", sensorWidth, sensorHeight, error);
        mci_camera_update(img_w, img_h, (uint8_t *)yBuf, (uint8_t *)uvBuf, 1);
    }

    // use buffer here,then callback will release buffer to camera.
    g_cam_callbk(g_cam_bufq, (void *)&bufIndex);
}

static void FlushCamBuffers(void)
{
    CAMLOGD("FlushCamBuffers \n");
}

unsigned mci_camera_preview(uint8_t *preview_buf, int preview_width, int preview_height)
{
    uint8_t rgb565_is_swap = 0;

#if (LV_COLOR_16_SWAP == 1)
    rgb565_is_swap = 1;
#endif

    CAMLOGD("%s, dirty:%d,y_buf:%p, wxh:%dx%d", __FUNCTION__, camera_info.dirty, camera_info.y_buf, preview_width, preview_height);
    if (camera_info.dirty) {
        camera_info.dirty = 0;

        if (IsChipCraneM() || IsChipCraneL())
        {
            char *yuvBuf = (char *)camera_info.y_buf;
            uint32_t img_w = preview_width;
            uint32_t img_h = preview_height;
        #if CAMERA_ROTATION_ENABLE
        #if CAMERA_ROT_DEG90_FLAG
            uint8_t rotate_status = ROT_90_DEG;
        #else
            uint8_t rotate_status = ROT_270_DEG;
        #endif

            if ((rotate_status == ROT_90_DEG) || (rotate_status == ROT_270_DEG))
            {
                img_w = preview_height;
                img_h = preview_width;
            }
        #endif

            if (g_mci_cam_yuv420_scaler_ptr)
            {
                // if need, do scaler
                if (0 <= MCI_CamSWYuv420nv12ScalerDown(g_mci_cam_yuv420_scaler_ptr, (uint32_t)img_w, (uint32_t)img_h, (char *)camera_info.y_buf, (uint32_t)camera_info.width, (uint32_t)camera_info.height))
                {
                    yuvBuf = g_mci_cam_yuv420_scaler_ptr;
                }
            }

        #if CAMERA_ROTATION_ENABLE
            if (g_mci_cam_yuv420_rotate_ptr)
            {
                // if need, do rotate
                if (0 <= MCI_CamSWYuv420nv12Rotate(g_mci_cam_yuv420_rotate_ptr, yuvBuf, (uint32_t)img_w, (uint32_t)img_h, rotate_status))
                {
                    yuvBuf = g_mci_cam_yuv420_rotate_ptr;
                }
            }
        #endif
            MCI_CamFullSizeStreamSaveBuffer(yuvBuf, (preview_width*preview_height*3/2));
            if (0 > MCI_CamSWYuv420ToRgb565((uint16_t *)preview_buf, preview_width, preview_height, yuvBuf, (uint32_t)preview_width, (uint32_t)preview_height, 1/*YUV_NV12*/, rgb565_is_swap))
                return 0;
            return 1;
        } else if ((preview_width == camera_info.width) && (preview_height == camera_info.height)) {
            yuv420_2_rgb565(camera_info.width, camera_info.height,
                (const unsigned char *)camera_info.y_buf, (unsigned short *)preview_buf,
                camera_info.is_semi);
            return 1;
        } else {
            int ret;
            uint8_t *pDst = (uint8_t *)lv_mem_aligned_alloc(preview_width*preview_height*3/2+32, 32);
            if (pDst == NULL) {
                printf("Failed:  mci_camera_preview no enough memory!!!\n");
                return 0;
            }

            ret = yuv420_scale(camera_info.width, camera_info.height, preview_width, preview_height,
                   camera_info.y_buf, pDst, camera_info.is_semi);
            if (ret == 1 ) {
                /* convert yuv to rgb */
                yuv420_2_rgb565(preview_width, preview_height,
                    (unsigned char *)pDst, (unsigned short *)preview_buf,
                    camera_info.is_semi);
            }
            lv_mem_aligned_free(pDst);

            return 1;
        }
    }
    return 0;
}

unsigned mci_camera_preview_yuv_size(int *width, int *height)
{
    CAMLOGD("%s, dirty:%d,y_buf:%p, wxh:%dx%d", __FUNCTION__, camera_info.dirty, camera_info.y_buf, camera_info.width, camera_info.height);
    *width    = camera_info.width;
    *height = camera_info.height;
    return 1;
}

unsigned mci_camera_preview_qrcode_scan(uint8_t *preview_buf, int preview_width, int preview_height)
{
    CAMLOGD("%s, dirty:%d,is_semi:%d,y_buf:%p, wxh:%dx%d, pre-wxh:%dx%d", __FUNCTION__, camera_info.dirty, camera_info.is_semi, camera_info.y_buf, camera_info.width, camera_info.height, preview_width, preview_height);
    if ((preview_width == camera_info.width) && (preview_height == camera_info.height)) {
        memcpy(preview_buf, camera_info.y_buf, preview_width*preview_height*3/2);
    } else if (IsChipCraneM() || IsChipCraneL()) {
        char * src_buf = (char *)camera_info.y_buf;
        uint32_t src_w = (uint32_t)camera_info.width;
        uint32_t src_h = (uint32_t)camera_info.height;
        uint32_t pre_w = (uint32_t)preview_width;
        uint32_t pre_h = (uint32_t)preview_height;

        if (0 > MCI_CamSWYuv420nv12ScalerDown((char *)preview_buf, pre_w, pre_h, src_buf, src_w, src_h))
        {
            CAMLOGE("%s,buf:%p(wxh:%dx%d), pre-buf:%p(wxh:%dx%d)", __FUNCTION__, src_buf, src_w, src_h, preview_buf, pre_w, pre_h);
        }
    } else {
        int ret;
        uint8_t *pDst = (uint8_t *)lv_mem_aligned_alloc(preview_width*preview_height*3/2+32, 32);
        if (pDst == NULL) {
            printf("Failed:  mci_camera_preview_qrcode_scan no enough memory!!!\n");
            return 0;
        }

        ret = yuv420_scale(camera_info.width, camera_info.height, preview_width, preview_height,
               camera_info.y_buf, pDst, camera_info.is_semi);
        if (ret == 1 ) {
            memcpy(preview_buf, pDst, preview_width*preview_height*3/2);
        }
        lv_mem_aligned_free(pDst);
    }
    return 1;
}

void mci_camera_preview_reset(void)
{
    CAMLOGD("%s, dirty:%d,y_buf:%p", __FUNCTION__, camera_info.dirty, camera_info.y_buf);
    memset(&camera_info, 0, sizeof(camera_info_t));
}

int mci_camer_setpara(int32_t effectCode, int32_t value)
{
    return MCI_CamSetPara(effectCode, value);
}

int mci_camera_start_full_size_stream(CAM_RECORD_PARMETERS_STRUCT *pRecordParam, uint32_t format)
{
    CCError error = CRANE_CAMERA_ERROR_NONE;
    int sensor_id = camera_sensor_id;
    uint32_t img_w = PREVIEW_SNAPSHOT_W;
    uint32_t img_h = PREVIEW_SNAPSHOT_H;
    uint32_t totalSize = 0;

    CAMLOGV("ready to ata open camera!");
    error = (CCError)MCI_CamOpenATA(sensor_id);

    if ( (error != CRANE_CAMERA_ERROR_NONE) && (error != CRANE_CAMERA_ERROR_DEVICEWASOPENED) ) {
        CAMLOGE("MCI_CamOpenATA the %d sensor failed for %d!", sensor_id, error);
        return -1;
    }

    error = (CCError)MCI_CamGetIspSize (&g_mci_cam_isp_size);
    if (error != CRANE_CAMERA_ERROR_NONE) {
        CAMLOGE("MCI_CamGetIspSize failed for %d!", error);
        goto Error_exit;
    }

    totalSize = (img_w * img_h) * 3 / 2;

    if ((g_mci_cam_isp_size.width != img_w) || (g_mci_cam_isp_size.height != img_h))
    {
        // need scaler
        if (!g_mci_cam_yuv420_scaler_ptr)
        {
            g_mci_cam_yuv420_scaler_ptr = (char*)lv_mem_aligned_alloc(totalSize + 32, 32);
            if (!g_mci_cam_yuv420_scaler_ptr)
            {
                printf("mci_camera_preview:scaler malloc %d fail!\n", totalSize);
                return 0;
            }
            memset(g_mci_cam_yuv420_scaler_ptr, 0x00, totalSize);
        }
    }

#if CAMERA_ROTATION_ENABLE
    if (!g_mci_cam_yuv420_rotate_ptr)
    {
        g_mci_cam_yuv420_rotate_ptr = (char*)lv_mem_aligned_alloc(totalSize + 32, 32);
        if (!g_mci_cam_yuv420_rotate_ptr)
        {
            printf("mci_camera_preview:rot malloc %d fail!\n", totalSize);
            return 0;
        }
        memset(g_mci_cam_yuv420_rotate_ptr, 0x00, totalSize);
    }
#endif

    CAMLOGV("ready to start full size!");
    error = (CCError)MCI_CamStartFullSizeStream((CAM_RECORD_PARMETERS_STRUCT*)pRecordParam, format);
    if (error != CRANE_CAMERA_ERROR_NONE) {
        CAMLOGE("start full size the %d sensor failed for %d!", sensor_id, error);
        goto Error_exit;
    }

    return 0;

Error_exit:
    MCI_CamCloseATA(sensor_id);
    CAMLOGE("Open the %d camera Error", sensor_id);
    return -1;
}

void mci_camera_stop_full_size_stream(void)
{
    CAMLOGD("%s",__FUNCTION__);
    int sensor_id = camera_sensor_id;

    MCI_CamStopFullSizeStream();
    MCI_CamCloseATA(sensor_id);

    memset(&g_mci_cam_isp_size, 0, sizeof(CAM_ISP_SIZE_STRUCT));
    if (g_mci_cam_yuv420_scaler_ptr)
    {
        lv_mem_aligned_free(g_mci_cam_yuv420_scaler_ptr);
        g_mci_cam_yuv420_scaler_ptr = NULL;
    }

#if CAMERA_ROTATION_ENABLE
    if (g_mci_cam_yuv420_rotate_ptr)
    {
        lv_mem_aligned_free(g_mci_cam_yuv420_rotate_ptr);
        g_mci_cam_yuv420_rotate_ptr = NULL;
    }
#endif
}

#if CAMERA_LCD_TEST
uint8_t   g_pip_id = 0;
uint8_t   g_buf_idx = 0;

void CameraHalTestInit(void)
{
    CCError error = CRANE_CAMERA_ERROR_NONE;
    CCStartPreviewStruct data;
    CCCaputreStruct capture_data;
    int sensor_id = 0;

    lcdc_poweron();
//    cam_offline_init();

    CAMLOGV("ready to open camera!");
    error = CC_OpenCamera(sensor_id);
    if (error != CRANE_CAMERA_ERROR_NONE) {
        CAMLOGE("CCOpenCamera the %d sensor failed for %d!", sensor_id, error);
        return;
    }

    CAMLOGV("ready to start preview!");
    data.image_width = 320;
    error = CC_StartPreview(&data);
    if (error != CRANE_CAMERA_ERROR_NONE) {
        CAMLOGE("CCStartPreview the %d sensor failed for %d!", sensor_id, error);
        goto Error_exit;
    }

//    mdelay(5000);
    CAMLOGV("ready to test capture!");
    capture_data.image_width = 240; ///320
    capture_data.image_height = 240;
    CC_TakePicture(&capture_data);
    mdelay(20);
    while (g_pip_id != PIPELINE_ID_CAP)
        mdelay(20);
//    CCCapturePortDone(g_pip_id, g_buf_idx);
    mdelay(120);
    CC_SavePicture();

//    while (1) {
//        mdelay(5000);
//    }

    //wait for camera stop flag
    CAMLOGV("wait stop flag!");
//    CCStopPreview();

Error_exit:
//    CCCloseCamera(sensor_id);
//    lcd_poweroff();
    CAMLOGV("close the %d camera successful!\n", sensor_id);
}
#endif
#endif

